const DATA_MTU = 256 - 3;

const GATAWAY_CHARACTERISTIC = "11fac9e0-c111-11e3-9246-0002a5d5c51b";
const SERVICE_ID = 0x180A;

export class BleHelper {
    device: BluetoothDevice;
    server?: BluetoothRemoteGATTServer;
    service?: BluetoothRemoteGATTService;

    constructor(device: BluetoothDevice) {
        this.device = device;
    }

    static async request(): Promise<BleHelper> {
        let options = {
            filters: [
                { namePrefix: 'MT-GW-' }
            ], optionalServices: [SERVICE_ID]
        }
        let na = navigator;
        let bt = na.bluetooth;
        let device = await bt.requestDevice(options);
        let util = new BleHelper(device);
        return util;
    }


    async connect() {
        if (this.device.gatt) {
            let gatt = this.device.gatt;
            this.server = await gatt.connect();
            this.service = await this.server.getPrimaryService(SERVICE_ID);
        }
    }

    async disconnect() {
        if (this.server) {
            this.server.disconnect();
        }
    }

    static async createDataViews(mtu: number, input: string): Promise<Array<ArrayBuffer>> {
        let result = new Array<ArrayBuffer>();
        let blob = new Blob([input]);
        if (blob.size !== 0) {
            let cnt = Math.ceil(blob.size / mtu);
            let start = 0;
            for (let i = 0; i < cnt; i++) {
                let end = Math.min(start + mtu, input.length);
                let b = blob.slice(start, end);
                let item = new Blob([String.fromCharCode(i), b])
                let ab = await item.arrayBuffer()
                result.push(ab)
                start += mtu;
            }
        }
        return result;
    }
    async send(msg: string) {
        let frames = await BleHelper.createDataViews(DATA_MTU - 1, msg);
        if (this.service) {
            let ch = await this.service.getCharacteristic(GATAWAY_CHARACTERISTIC);
            for (let v of frames) {
                await ch.writeValue(v);
            }
        }
    }

    async read(): Promise<string> {
        let received: Array<string> = []
        if (this.service) {
            let ch = await this.service.getCharacteristic(GATAWAY_CHARACTERISTIC);
            do {
                const result = await ch.readValue();
                for (let i = 0; i < result.byteLength; i++) {
                    let c = result.getUint8(i);
                    received.push(String.fromCharCode(c));
                }
                if (result.byteLength < DATA_MTU) {
                    break;
                }
            } while (true);
        }
        let msg = received.join("");
        return msg;
    };
}